import {
  AlignmentType,
  Document,
  HeadingLevel,
  ImageRun,
  Packer,
  Paragraph,
  Table,
  TableCell,
  TableRow,
  TextRun,
  UnderlineType,
} from 'docx';
import { promises as fs } from 'fs';
import type MarkdownIt from 'markdown-it';
import path from 'path';
import { DocumentBrowserManagerPuppeteer } from './browser-manager-puppeteer.js';
import { PersistentMermaidRenderer } from './mermaid-persistent.js';
import type { ConversionOptions, ConversionResult, DocxOptions } from './types.js';

/**
 * Markdown转DOCX工具类
 * 支持中文字体、Mermaid图表转图片嵌入
 */
export class MarkdownToDocx {
  private options: Required<DocxOptions>;
  private md!: MarkdownIt;
  private mermaidCounter: number = 0;
  private browserManager: DocumentBrowserManagerPuppeteer = new DocumentBrowserManagerPuppeteer();

  constructor(options: DocxOptions = {}) {
    this.options = {
      // 文档属性
      title: 'Document',
      creator: 'Markdown Converter',
      description: 'Generated from Markdown',
      // 中文字体配置
      chineseFont: 'Microsoft YaHei',
      englishFont: 'Calibri',
      codeFont: 'Consolas',
      // 样式配置
      fontSize: 22, // 11pt = 22 half-points
      headingColor: '2F5597',
      linkColor: '0563C1',
      // Mermaid配置
      mermaidWidth: 600,
      mermaidHeight: 400,
      ...options,
    };

    // 动态导入markdown-it
    this.initializeMarkdownIt();
  }

  private async initializeMarkdownIt(): Promise<void> {
    const MarkdownItClass = (await import('markdown-it')).default;

    this.md = new MarkdownItClass({
      html: false, // DOCX不需要HTML
      linkify: true,
      typographer: true,
    });
  }

  /**
   * 渲染Mermaid图表为图片
   */
  private async renderMermaidToImage(
    mermaidCode: string,
    outputDir: string
  ): Promise<string | null> {
    try {
      this.mermaidCounter++;
      const imagePath = path.join(outputDir, `mermaid-${this.mermaidCounter}.png`);
      console.log(`🔄 开始处理第${this.mermaidCounter}个Mermaid图表`);

      const usePersist = String(process.env.MERMAID_PERSIST || '').toLowerCase() === 'true';

      let result: string | null = null;
      if (usePersist) {
        // 优先使用持久化渲染器
        result = await PersistentMermaidRenderer.instance.renderToImage(mermaidCode, imagePath, {
          width: this.options.mermaidWidth,
          height: this.options.mermaidHeight,
          chineseFont: this.options.chineseFont,
          theme: 'default',
        });
      } else {
        // 回退到一次性渲染
        result = await this.browserManager.renderMermaidToImage(mermaidCode, imagePath, {
          width: this.options.mermaidWidth,
          height: this.options.mermaidHeight,
          chineseFont: this.options.chineseFont,
          theme: 'default',
        });
      }

      if (result && !result.startsWith('error:')) {
        console.info(`✅ 第${this.mermaidCounter}个Mermaid图表处理成功`);
      } else if (result?.startsWith('error:')) {
        console.warn(`⚠️ 第${this.mermaidCounter}个Mermaid图表处理失败: ${result.substring(6)}`);
      } else {
        console.warn(`⚠️ 第${this.mermaidCounter}个Mermaid图表处理返回null`);
      }

      return result;
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error);
      console.warn(`❌ Mermaid图表渲染失败: ${errorMessage}`);
      return null;
    }
  }

  /**
   * 解析Markdown tokens并转换为DOCX元素
   */
  private async parseTokens(tokens: any[], outputDir: string): Promise<any[]> {
    const elements: any[] = [];
    let currentList: any[] | null = null;
    let listLevel = 0;

    for (let i = 0; i < tokens.length; i++) {
      const token = tokens[i];

      switch (token.type) {
        case 'heading_open':
          const level = parseInt(token.tag.charAt(1));
          const headingToken = tokens[i + 1];
          if (headingToken && headingToken.type === 'inline') {
            elements.push(this.createHeading(headingToken.content, level));
          }
          i++; // 跳过heading_close
          break;

        case 'paragraph_open':
          const pToken = tokens[i + 1];
          if (pToken && pToken.type === 'inline') {
            const paragraph = await this.createParagraph(pToken.children, outputDir);
            if (paragraph) {
              elements.push(paragraph);
            }
          }
          i++; // 跳过paragraph_close
          break;

        case 'bullet_list_open':
        case 'ordered_list_open':
          currentList = [];
          listLevel++;
          break;

        case 'bullet_list_close':
        case 'ordered_list_close':
          if (currentList) {
            elements.push(...currentList);
            currentList = null;
            listLevel--;
          }
          break;

        case 'list_item_open':
          const listItemTokens = this.extractListItemTokens(tokens, i);
          if (listItemTokens.length > 0) {
            const listItem = await this.createListItem(listItemTokens, outputDir, listLevel);
            if (listItem && currentList) {
              currentList.push(listItem);
            }
          }
          break;

        case 'table_open':
          const tableTokens = this.extractTableTokens(tokens, i);
          const table = await this.createTable(tableTokens, outputDir);
          if (table) {
            elements.push(table);
          }
          // 跳到table_close
          while (i < tokens.length && tokens[i].type !== 'table_close') {
            i++;
          }
          break;

        case 'blockquote_open':
          const blockquoteTokens = this.extractBlockquoteTokens(tokens, i);
          const blockquotes = await this.createBlockquote(blockquoteTokens, outputDir);
          if (blockquotes) {
            elements.push(...blockquotes);
          }
          // 跳到blockquote_close
          while (i < tokens.length && tokens[i].type !== 'blockquote_close') {
            i++;
          }
          break;

        case 'fence':
          if (token.info === 'mermaid') {
            const mermaidImage = await this.renderMermaidToImage(token.content, outputDir);
            if (mermaidImage && !mermaidImage.startsWith('error:')) {
              const imageParagraph = await this.createMermaidParagraph(mermaidImage);
              elements.push(imageParagraph);
            } else if (mermaidImage?.startsWith('error:')) {
              // Mermaid渲染失败时添加错误提示
              elements.push(
                new Paragraph({
                  children: [
                    new TextRun({
                      text: `[Mermaid图表渲染失败: ${mermaidImage.substring(6)}]`,
                      color: 'FF0000',
                    }),
                  ],
                })
              );
            }
          } else {
            elements.push(...this.createCodeBlock(token.content, token.info));
          }
          break;

        case 'code_block':
          elements.push(...this.createCodeBlock(token.content));
          break;

        case 'hr':
          elements.push(this.createHorizontalRule());
          break;
      }
    }

    return elements;
  }

  /**
   * 创建标题
   */
  private createHeading(text: string, level: number): Paragraph {
    const headingLevel = Math.min(level, 6);
    const headingLevels = [
      HeadingLevel.TITLE,
      HeadingLevel.HEADING_1,
      HeadingLevel.HEADING_2,
      HeadingLevel.HEADING_3,
      HeadingLevel.HEADING_4,
      HeadingLevel.HEADING_5,
      HeadingLevel.HEADING_6,
    ];

    return new Paragraph({
      text: text,
      heading: headingLevels[headingLevel],
      spacing: {
        before: headingLevel === 1 ? 400 : 240,
        after: 120,
      },
    });
  }

  /**
   * 创建段落
   */
  private async createParagraph(inlineTokens: any[], outputDir: string): Promise<Paragraph | null> {
    const runs: TextRun[] = [];
    let currentStyle: any = {};

    for (const token of inlineTokens) {
      switch (token.type) {
        case 'text':
          runs.push(
            new TextRun({
              text: token.content,
              font: {
                ascii: this.options.englishFont,
                eastAsia: this.options.chineseFont,
              },
              size: this.options.fontSize,
              ...currentStyle,
            })
          );
          break;

        case 'strong_open':
          currentStyle.bold = true;
          break;

        case 'strong_close':
          currentStyle.bold = false;
          break;

        case 'em_open':
          currentStyle.italics = true;
          break;

        case 'em_close':
          currentStyle.italics = false;
          break;

        case 'code_inline':
          runs.push(
            new TextRun({
              text: token.content,
              font: {
                ascii: this.options.codeFont,
                eastAsia: this.options.codeFont,
              },
              size: this.options.fontSize - 2,
              color: '24292E',
              highlight: 'yellow',
            })
          );
          break;

        case 'link_open':
          const href = token.attrGet ? token.attrGet('href') : '';
          currentStyle.color = this.options.linkColor;
          currentStyle.underline = { type: UnderlineType.SINGLE };
          break;

        case 'link_close':
          delete currentStyle.color;
          delete currentStyle.underline;
          break;
      }
    }

    if (runs.length === 0) return null;

    return new Paragraph({
      children: runs,
      spacing: {
        after: 120,
      },
    });
  }

  /**
   * 创建列表项
   */
  private async createListItem(
    tokens: any[],
    outputDir: string,
    level: number
  ): Promise<Paragraph | null> {
    const runs: TextRun[] = [];

    for (const token of tokens) {
      if (token.type === 'inline') {
        for (const childToken of token.children) {
          if (childToken.type === 'text') {
            runs.push(
              new TextRun({
                text: childToken.content,
                font: {
                  ascii: this.options.englishFont,
                  eastAsia: this.options.chineseFont,
                },
                size: this.options.fontSize,
              })
            );
          }
        }
      }
    }

    if (runs.length === 0) return null;

    return new Paragraph({
      children: runs,
      bullet: {
        level: level - 1,
      },
      spacing: {
        after: 60,
      },
    });
  }

  /**
   * 创建表格
   */
  private async createTable(tableTokens: any[], outputDir: string): Promise<Table | null> {
    const rows: TableRow[] = [];
    let isHeader = true;

    for (const token of tableTokens) {
      if (token.type === 'tr_open') {
        const rowTokens = this.extractTableRowTokens(tableTokens, tableTokens.indexOf(token));
        const cells: TableCell[] = [];

        for (const rowToken of rowTokens) {
          if (rowToken.type === 'td_open' || rowToken.type === 'th_open') {
            const cellTokens = this.extractTableCellTokens(rowTokens, rowTokens.indexOf(rowToken));
            let cellText = '';

            for (const cellToken of cellTokens) {
              if (cellToken.type === 'inline') {
                cellText += cellToken.content;
              }
            }

            cells.push(
              new TableCell({
                children: [
                  new Paragraph({
                    children: [
                      new TextRun({
                        text: cellText,
                        font: {
                          ascii: this.options.englishFont,
                          eastAsia: this.options.chineseFont,
                        },
                        size: this.options.fontSize,
                        bold: isHeader,
                      }),
                    ],
                    alignment: AlignmentType.LEFT,
                  }),
                ],
                margins: {
                  top: 100,
                  bottom: 100,
                  left: 100,
                  right: 100,
                },
              })
            );
          }
        }

        if (cells.length > 0) {
          rows.push(
            new TableRow({
              children: cells,
              cantSplit: true,
            })
          );
        }

        isHeader = false; // 第一行后不再是表头
      }
    }

    if (rows.length === 0) return null;

    return new Table({
      rows: rows,
      width: {
        size: 100,
        type: 'pct' as any,
      },
    });
  }

  /**
   * 创建代码块
   */
  private createCodeBlock(content: string, language = ''): Paragraph[] {
    // 按行分割代码内容，保持原始换行
    const lines = content.split(/\r?\n/);
    const paragraphs: Paragraph[] = [];

    lines.forEach((line, index) => {
      paragraphs.push(
        new Paragraph({
          children: [
            new TextRun({
              text: line || ' ', // 空行使用空格占位
              font: {
                ascii: this.options.codeFont,
                eastAsia: this.options.codeFont,
              },
              size: this.options.fontSize - 2,
              color: '24292E',
            }),
          ],
          border: {
            top: index === 0 ? { style: 'single' as any, size: 1, color: 'CCCCCC' } : undefined,
            bottom: index === lines.length - 1 ? { style: 'single' as any, size: 1, color: 'CCCCCC' } : undefined,
            left: { style: 'single' as any, size: 1, color: 'CCCCCC' },
            right: { style: 'single' as any, size: 1, color: 'CCCCCC' },
          },
          shading: {
            fill: 'F6F8FA',
          },
          spacing: {
            before: index === 0 ? 120 : 0,
            after: index === lines.length - 1 ? 120 : 0,
            line: 240, // 行间距，相当于1.2倍
          },
        })
      );
    });

    return paragraphs;
  }

  /**
   * 创建Mermaid图片段落
   */
  private async createMermaidParagraph(imagePath: string): Promise<Paragraph> {
    try {
      const imageBuffer = await fs.readFile(imagePath);

      return new Paragraph({
        children: [
          new ImageRun({
            data: imageBuffer,
            transformation: {
              width: this.options.mermaidWidth * 0.6,
              height: this.options.mermaidHeight * 0.6,
            },
            type: 'png' as any,
          }),
        ],
        alignment: AlignmentType.CENTER,
        spacing: {
          before: 200,
          after: 200,
        },
      });
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error);
      console.warn(`无法嵌入Mermaid图片: ${errorMessage}`);
      return new Paragraph({
        children: [
          new TextRun({
            text: `[Mermaid图表渲染失败: ${errorMessage}]`,
            color: 'FF0000',
          }),
        ],
      });
    }
  }

  /**
   * 创建水平线
   */
  private createHorizontalRule(): Paragraph {
    return new Paragraph({
      border: {
        bottom: {
          style: 'single' as any,
          size: 6,
          color: 'CCCCCC',
        },
      },
      spacing: {
        before: 200,
        after: 200,
      },
    });
  }

  /**
   * 创建引用块
   */
  private async createBlockquote(tokens: any[], outputDir: string): Promise<Paragraph[]> {
    const paragraphs: Paragraph[] = [];

    for (let i = 0; i < tokens.length; i++) {
      const token = tokens[i];
      if (token.type === 'paragraph_open') {
        const inlineIndex = i + 1;
        if (inlineIndex < tokens.length && tokens[inlineIndex].type === 'inline') {
          // 创建带有引用样式的段落
          const runs: TextRun[] = [];
          for (const childToken of tokens[inlineIndex].children) {
            if (childToken.type === 'text') {
              runs.push(
                new TextRun({
                  text: childToken.content,
                  font: {
                    ascii: this.options.englishFont,
                    eastAsia: this.options.chineseFont,
                  },
                  size: this.options.fontSize,
                  italics: true, // 引用内容使用斜体
                })
              );
            }
          }

          if (runs.length > 0) {
            const quoteParagraph = new Paragraph({
              children: runs,
              indent: { left: 720 }, // 0.5 inch 缩进
              border: {
                left: { style: 'single' as any, size: 6, color: this.options.linkColor },
              },
              spacing: { after: 120 },
            });
            paragraphs.push(quoteParagraph);
          }
        }
      }
    }

    return paragraphs;
  }

  // 辅助方法：提取特定类型的tokens
  private extractListItemTokens(tokens: any[], startIndex: number): any[] {
    const extracted: any[] = [];
    let depth = 0;

    for (let i = startIndex; i < tokens.length; i++) {
      const token = tokens[i];

      if (token.type === 'list_item_open') depth++;
      if (token.type === 'list_item_close') depth--;

      extracted.push(token);

      if (depth === 0 && token.type === 'list_item_close') break;
    }

    return extracted;
  }

  private extractTableTokens(tokens: any[], startIndex: number): any[] {
    const extracted: any[] = [];
    let depth = 0;

    for (let i = startIndex; i < tokens.length; i++) {
      const token = tokens[i];

      if (token.type === 'table_open') depth++;
      if (token.type === 'table_close') depth--;

      extracted.push(token);

      if (depth === 0 && token.type === 'table_close') break;
    }

    return extracted;
  }

  private extractTableRowTokens(tokens: any[], startIndex: number): any[] {
    const extracted: any[] = [];
    let depth = 0;

    for (let i = startIndex; i < tokens.length; i++) {
      const token = tokens[i];

      if (token.type === 'tr_open') depth++;
      if (token.type === 'tr_close') depth--;

      extracted.push(token);

      if (depth === 0 && token.type === 'tr_close') break;
    }

    return extracted;
  }

  private extractTableCellTokens(tokens: any[], startIndex: number): any[] {
    const extracted: any[] = [];
    let depth = 0;

    for (let i = startIndex; i < tokens.length; i++) {
      const token = tokens[i];

      if (token.type === 'td_open' || token.type === 'th_open') depth++;
      if (token.type === 'td_close' || token.type === 'th_close') depth--;

      extracted.push(token);

      if (depth === 0 && (token.type === 'td_close' || token.type === 'th_close')) break;
    }

    return extracted;
  }

  private extractBlockquoteTokens(tokens: any[], startIndex: number): any[] {
    const extracted: any[] = [];
    let depth = 0;

    for (let i = startIndex; i < tokens.length; i++) {
      const token = tokens[i];

      if (token.type === 'blockquote_open') depth++;
      if (token.type === 'blockquote_close') depth--;

      extracted.push(token);

      if (depth === 0 && token.type === 'blockquote_close') break;
    }

    return extracted;
  }

  /**
   * 转换Markdown为DOCX
   */
  async convertToDocx(
    markdownContent: string,
    outputPath: string,
    options: ConversionOptions = {}
  ): Promise<ConversionResult> {
    const convertOptions = { ...this.options, ...options };

    try {
      // 创建临时目录用于存储Mermaid图片
      const tempDir = path.join(path.dirname(outputPath), '.temp-mermaid');
      await fs.mkdir(tempDir, { recursive: true });

      // 确保markdown-it已初始化
      if (!this.md) {
        await this.initializeMarkdownIt();
      }

      // 解析Markdown
      const tokens = this.md.parse(markdownContent, {});

      // 转换为DOCX元素
      const elements = await this.parseTokens(tokens, tempDir);

      // 创建DOCX文档
      const doc = new Document({
        creator: convertOptions.creator,
        title: convertOptions.title,
        description: convertOptions.description,
        sections: [
          {
            children: elements.flat(), // 展开可能的嵌套数组
          },
        ],
        styles: {
          default: {
            document: {
              run: {
                font: {
                  ascii: convertOptions.englishFont,
                  eastAsia: convertOptions.chineseFont,
                },
                size: convertOptions.fontSize,
              },
            },
          },
        },
      });

      // 生成DOCX buffer
      const buffer = await Packer.toBuffer(doc);

      // 写入文件
      await fs.writeFile(outputPath, buffer);

      // 清理临时目录
      try {
        await fs.rmdir(tempDir, { recursive: true });
      } catch (cleanupError) {
        console.warn(`清理临时目录失败: ${cleanupError}`);
      }

      return {
        success: true,
        outputPath,
        size: buffer.length,
        message: `DOCX生成成功: ${outputPath}`,
      };
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error);
      console.error('DOCX生成失败:', errorMessage);
      throw new Error(`DOCX生成失败: ${errorMessage}`);
    }
  }

  /**
   * 从文件转换
   */
  async convertFromFile(
    markdownFilePath: string,
    outputPath: string,
    options: ConversionOptions = {}
  ): Promise<ConversionResult> {
    try {
      const markdownContent = await fs.readFile(markdownFilePath, 'utf8');
      const title = options.title || path.basename(markdownFilePath, '.md');

      return await this.convertToDocx(markdownContent, outputPath, { ...options, title });
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error);
      throw new Error(`读取Markdown文件失败: ${errorMessage}`);
    }
  }
}

/**
 * 便捷函数：直接转换Markdown内容为DOCX
 */
export async function markdownToDocx(
  markdownContent: string,
  outputPath: string,
  options: DocxOptions & ConversionOptions = {}
): Promise<ConversionResult> {
  const converter = new MarkdownToDocx(options);
  return await converter.convertToDocx(markdownContent, outputPath, options);
}

/**
 * 便捷函数：从文件转换为DOCX
 */
export async function markdownFileToDocx(
  markdownFilePath: string,
  outputPath: string,
  options: DocxOptions & ConversionOptions = {}
): Promise<ConversionResult> {
  const converter = new MarkdownToDocx(options);
  return await converter.convertFromFile(markdownFilePath, outputPath, options);
}
